from flask import Blueprint, request, jsonify
from utils.utils import build_fail_response, confirm_params_type, build_success_response
import os
import re
from database.db_tools import SQLServerDatabase, connection_string

# 创建蓝图
service_page = Blueprint('service_page', __name__)




def list_files_in_folder(folder):
    local_file_paths = []
    for root, dirs, files in os.walk(folder):
        for file in files:
            file_path = os.path.join(root, file)
            local_file_paths.append(file_path)
    return local_file_paths

# 获取符合要求的全部路径
def list_all_files(root_folder):
    file_paths = set()  # Use a set for uniqueness

    for root, dirs, files in os.walk(root_folder):
        local_file_paths = list_files_in_folder(root)
        file_paths.update(local_file_paths)  # Add local paths to the set
    return list(file_paths)

# 存在 订单号后面加上数量的单， CG2210274450-CDNC-220P ，只能通过CG2210274450-CDNC去获取当前路径，缩小范围
def get_min_path(root_folder, substring):
    # 获取 root_folder 下的所有文件夹
    folders = [f for f in os.listdir(root_folder) if os.path.isdir(os.path.join(root_folder, f))]

    # 在文件夹中查找符合条件的文件
    for folder in folders:
        if folder.startswith(substring) or folder == substring:
            return os.path.join(root_folder, folder)
    return ''

# 通过 订单号+sn 确定唯一的bin文件路径 并取出共享文件夹里面的数据
@service_page.route('/pyapi/getSharePublicDataBin', methods=['POST'])
def getSharePublicDataBin():
    try:
        orderNumber = request.json.get('orderNumber')
        sn = request.json.get('sn')
        date = request.json.get('date')
        nas_path = request.json.get('nas_path')
        variable_path = os.path.join(nas_path, date)
        # 通过订单号缩小最小路径范围
        full_path = get_min_path(variable_path, orderNumber)

        # 如果没有找到包含orderNumber的文件下，直接结束程序
        if len(full_path) == 0:
            return {
                'sn': sn,
                'standard_string': '',
                'binPath': os.path.join(variable_path, orderNumber)
            }
        # print('full_path', full_path)

        # 找到的目标sn路径
        all_files = list_all_files(full_path)
        bininfo = [s.strip() for s in all_files if sn in s]

        if len(bininfo) == 1:
            with open(bininfo[0], 'rb') as f:
                data = f.read()
                hex_data = data.hex()
            res = {
                'sn': sn,
                'standard_string': hex_data,
                'binPath': bininfo[0]
            }
        else:
            res = {
                'sn': sn,
                'standard_string': '',
                'binPath': full_path
            }
        return build_success_response(data=res)
    except Exception as e:
        return build_fail_response(message=str(e), params=confirm_params_type(request))

# -----------------------------------------------------------------------------------------------------------------------------------------------------------------------
# 通过 订单号+sn 从共享数据库中获取bin文件的完整路径
@service_page.route('/pyapi/getBinFromPublicPath', methods=['GET'])
def getBinFromPublicPath():
    try:
        orderNumber = request.args.get('orderNumber')
        sn = request.args.get('sn')
        # 操作数据库
        with SQLServerDatabase(connection_string) as db:
            # 无需分页，查询全部
            # res = db.execute_query_all("test", filters="binpath LIKE ? AND binpath LIKE ?", params=['%\\' + orderNumber + '\\%', '%'+sn])
            res = db.execute_query_all("test", filters="binname = ?",
                                       params=[sn])

            return jsonify(res)
    except Exception as e:
        return build_fail_response(message=str(e), params=confirm_params_type(request))

@service_page.route('/pyapi/compareBinByPublicAndMoudleBin', methods=['POST'])
def compareBinByPublicAndMoudleBin() :
    try:
        sn = request.json.get('sn')
        publicPath = request.json.get('publicPath')
        compare_string = request.json.get('compare_string')
        specialCodePathDirName = request.json.get('specialCodePathDirName')


        # publicPath 如果该路径没有到bin文件，只是到文件夹，则需要进一步进行查找处理
        # publicPath = r'\\10.36.230.108\复检测试路径\2024\6月\28\CW20240626115843-AU2'
        # sn = 'EA132500050711'

        # 遍历目录及其子目录
        full_path = None
        # 文件夹
        if publicPath.endswith('.bin') is False:
            for root, dirs, files in os.walk(publicPath):
                for file_name in files:
                    if file_name.endswith(sn+'.bin'):
                        full_path = os.path.join(root, file_name)
                        break
                if full_path:
                    break

            # 检查路径是否可访问
            if full_path and os.path.exists(full_path):
                print(f"找到文件: {full_path}")
                publicPath = full_path
            else:
                print("没有找到匹配的文件或路径不可访问。")
                return build_fail_response(message='请检查指定的文件夹路径是否正确！', params=confirm_params_type(request))


        # 解析共享文件夹的16进制
        with open(publicPath, 'rb') as f:
            data = f.read()
            # 转为大写
            standard_string = data.hex().upper()


        # 对原16进制数据进行字符串翻译
        standard_string_parse = binToParse(standard_string.replace(" ", ""))
        compare_string_parse = binToParse(compare_string.replace(" ", ""))

        # 比较字符串并获取结果
        formatted_compare_string, differences = compare_hex_strings(standard_string, compare_string, specialCodePathDirName, standard_string_parse, compare_string_parse)

        # 去除 standard_string 中的所有空格
        formatted_standard_string = standard_string.replace(" ", "")

        # 格式化 standard_string 的展示格式
        formatted_standard_string = ' '.join(
            [formatted_standard_string[i:i + 2] for i in range(0, len(formatted_standard_string), 2)])
        formatted_standard_string = '<br>'.join(
            [formatted_standard_string[i:i + 48] for i in range(0, len(formatted_standard_string), 48)]).strip()

        result = {
            'standard_string': formatted_standard_string,
            'compare_string': formatted_compare_string,
            'standard_string_parse': standard_string_parse,
            'compare_string_parse': compare_string_parse,
            'differences': differences,
            'shareRes': bool(len(differences) == 0)
        }

        # return jsonify(result)
        return build_success_response(data=result)
    except Exception as e:
        return build_fail_response(message=str(e), params=confirm_params_type(request))
# -------------------------------------------------------------------------------------------------------------------------------------------------------------------------
# 对bin文件进行字符串解析
def binToParse(hex_string):
    hex_chunks = [hex_string[i:i+32] for i in range(0, len(hex_string), 32)]
    result_strings = []
    for chunk in hex_chunks:
        byte_chunk = bytes.fromhex(chunk)
        printable_chars = [chr(byte) if 32 <= byte < 127 else '.' for byte in byte_chunk]
        result_strings.append(''.join(printable_chars))
    parsed_string = '<br>'.join(result_strings)
    return parsed_string

# 判断字符串的后一半全是由0或者F组成的，不进行对比
def is_all_zero_or_f(s):
    half_length = len(s) // 2
    second_half = s[half_length:]
    return all(c in ("0", "F") for c in second_half)

def hex_to_string(hex_string):
    try:
        decoded_string = bytes.fromhex(hex_string).decode("utf-8")
    except UnicodeDecodeError:
        decoded_string = ""
        for char in bytes.fromhex(hex_string):
            if char < 32 or char > 126:  # 控制字符或非 ASCII 字符
                decoded_string += "."
            else:
                decoded_string += chr(char)
    return decoded_string

# 处理特殊情况。当指定码位为FF或者00或者全是一样的时，不参与比较def validate_string(s, start_idx, end_idx):
def validate_string(s, start_idx, end_idx):
    # 先根据给定的范围截取字符串
    sub_str = s[start_idx-1:end_idx]
    # 检查是否整个字符串是相同的字符
    if all(c == sub_str[0] for c in sub_str):
        return True
    # 检查长度是否是偶数，以保证可以每两个字符一组
    if len(sub_str) % 2 != 0:
        return False
    # 遍历截取的字符串，每次步进2
    for i in range(0, len(sub_str), 2):
        # 检查每两个字符的组合
        if sub_str[i:i+2] not in ['FF', '00']:
            return False
    return True


def get_middle_part(text, delimiter='<br>', num_parts=3):
    """
    将字符串按指定的分隔符分为多个部分，并返回中间的那部分文本。

    :param text: 待分割的字符串
    :param delimiter: 分隔符，默认为'<br>'
    :param num_parts: 分割的部分数量，默认为3
    :return: 中间部分的文本
    """
    # 将字符串按照指定分隔符分割成列表
    lines = text.split(delimiter)

    # 计算每部分的长度
    total_lines = len(lines)
    part_size = total_lines // num_parts  # 每部分的行数

    # 切分为多个部分
    parts = [lines[i * part_size:(i + 1) * part_size] for i in range(num_parts - 1)]
    parts.append(lines[(num_parts - 1) * part_size:])  # 最后一部分

    # 获取中间的部分
    middle_part = parts[num_parts // 2]

    # 将中间部分重新拼接为字符串
    middle_part_text = delimiter.join(middle_part)

    return middle_part_text
def compare_hex_strings(standard_string, compare_string, specialCodePathDirName, standard_string_parse, compare_string_parse):
    # 去掉空格并按照两位数为一组进行比较
    standard_string = standard_string.replace(" ", "")
    compare_string = compare_string.replace(" ", "")

    differences = []
    compared_string = ""

    # "Byte0为03，Byte2为00/21/22/23时，BIN比较只核对前面256位 （若为H3-DAC、HU-AOC则无法涵盖"）
    start_with_03_and_Byte2_is_00_21_22_23 = standard_string.startswith("03") and standard_string[4:6] in ['00', '21',
                                                                                                           '22', '23']
    # Check if standard_string starts with "03", "0B", "18", or "19"
    start_with_03_or_0B = standard_string.startswith("03") or standard_string.startswith("0B")
    start_with_18_or_19_or_1E = standard_string.startswith("18") or standard_string.startswith(
        "19") or standard_string.startswith("1E")
    # Byte0为0D、11时，如果box读取第1-128位和第129-256重复时，则只比较1-128位，反之则全部比对
    start_with_0D_or_11 = standard_string.startswith("0D") or standard_string.startswith("11")
    strat_with_06 = standard_string.startswith("06")
    # 线缆的只分为两种情况：
    # 1、当型号名Q28-XX或者QSFP-XX时，并且不是CO兼容和H3C兼容时，比较前128
    # 2、其它情况都只比较前256

    # < br > ....F2033817600 - < br > 1  240102... + < br >
    #  根据规律写出  正则表达式模式
    pattern = "-<br>[0-9]   2"

    # 使用re.search()进行匹配
    isCable = re.search(pattern, standard_string_parse)
    # 如果找到了匹配，说明是线缆，否则不是线缆
    # 线缆

    # 需求又有变化，有些特殊的情况，是不区分线缆和非线缆的，，现在把这些特殊的统一当成非线缆去处理
    # 详情见飞书文档 https://mah2eds8ab.feishu.cn/sheets/KOB8sTPVfhpom4tgLcccRBwfncf?sheet=Fa12OE  线缆比较规则

    if isCable is not None and not start_with_0D_or_11 and not start_with_03_and_Byte2_is_00_21_22_23 and not start_with_18_or_19_or_1E:
        for (index, i) in enumerate(range(0, len(standard_string), 2)):

            char1 = standard_string[i:i + 2]
            char2 = compare_string[i:i + 2]

            # 1、当型号名Q28 - XX或者QSFP - XX时，并且不是CO兼容和H3C兼容时，比较前128
            if (
                    'Q28-' in standard_string_parse or 'QSFP-' in standard_string_parse) and 'CO' not in standard_string_parse and 'H3C' not in standard_string_parse:
                if (index >= 0 and index <= 127):
                    if char1 != char2:
                        differences.append(i // 2)
                        compared_string += "<span style='color: red;font-weight: bold;'>{}</span> ".format(char2)
                    else:
                        compared_string += char2 + " "
                else:
                    compared_string += char2 + " "
            # 2、其它情况都只比较前256
            else:
                # 存在特殊情况
                # 当码位出现数值FINISAR CORP时，需比对A0低位和A2高位, A0高位无需比对 （前128位和后128位）
                # 存在变化，当除了前128位必须比较，后面的码位如果由FF或者00组成时，也不比较，或者全是一样的时也不比较
                if 'FINISAR CORP' in standard_string_parse:
                    # 因为是每两个字符为一组，所以validate_string截取值从1开始的，传入的start end参数应该是正常索引值 X 2
                    # print(validate_string('030407100000000000000006670000000804000046494E4953415220434F52502E2020200000906546544C5838353734443342434C202020412020200352002E001A000043323430363037363535302D32202020323430363237202068F005F100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FFFFFFFFFFFFFFFF', 1, 256))  # False 0-128
                    # print(validate_string('030407100000000000000006670000000804000046494E4953415220434F52502E2020200000906546544C5838353734443342434C202020412020200352002E001A000043323430363037363535302D32202020323430363237202068F005F100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FFFFFFFFFFFFFFFF', 257,512)) # False 129-256
                    # print(validate_string('030407100000000000000006670000000804000046494E4953415220434F52502E2020200000906546544C5838353734443342434C202020412020200352002E001A000043323430363037363535302D32202020323430363237202068F005F100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000FFFFFFFFFFFFFFFF', 513,768)) # True 257-384
                    if (index >= 0 and index <= 127) or (
                            index >= 128 and index <= 255 and not validate_string(compare_string, 257, 512) and len(
                            compare_string) >= 512) or (
                            index >= 256 and index <= 383 and not validate_string(compare_string, 513, 768) and len(
                            compare_string) >= 768):
                        if char1 != char2:
                            differences.append(i // 2)
                            compared_string += "<span style='color: red;font-weight: bold;'>{}</span> ".format(
                                char2)
                        else:
                            compared_string += char2 + " "
                    else:
                        compared_string += char2 + " "
                else:
                    if (index >= 0 and index <= 255):
                        if char1 != char2:
                            differences.append(i // 2)
                            compared_string += "<span style='color: red;font-weight: bold;'>{}</span> ".format(
                                char2)
                        else:
                            compared_string += char2 + " "
                    else:
                        compared_string += char2 + " "

            if (i + 2) % 32 == 0:
                compared_string += "<br>"

    # 非线缆
    else:

        for (index, i) in enumerate(range(0, len(standard_string), 2)):

            char1 = standard_string[i:i + 2]
            char2 = compare_string[i:i + 2]

            # print('i',index,i,char1)
            # i 0 0 18
            # i 1 2 33
            # i 2 4 66
            # i 3 6 66
            # i 4 8 34

            # Byte0为0D、11时，如果box读取第1-128位和第129-256重复时，则只比较1-128位，反之则全部比对
            # 如果后128位全部是00或者FF的时候只比较前128位
            if start_with_0D_or_11:
                #  定义一个正则表达式，匹配前一半和后一半完全相等的字符串,字符串一定为双数
                pattern = r"^(\w+)\1$"
                match1 = re.search(pattern, compare_string)
                # 多加一个判断，如果共享文件里面的标准码也存在前128位和后128位相同时，只比较前128位
                match2 = re.search(pattern, standard_string)
                # 右侧基本信息包含Mellanox字符串时
                # 只复检比对前128位信息，后128位无需比对
                isHavingMellanox = 'Mellanox' in standard_string_parse

                # 右侧基本信息包含QSFPDD-2Q56PC015字符串时
                # 只复检比对前128位信息，后128位无需比对
                isHavingQSFPDD2Q56PC015 = 'QSFPDD-2<br>Q56PC015' in standard_string_parse

                # 200G QSFP 封装的线缆只写128位，A2高位不做对比
                # 当右侧后128位出现“127FMA627L3415”字样时，只比对前128位
                is127FMA627L3415 = '127FMA627L3415' in standard_string_parse

                # 新增对比条件
                # 当compare_string专为字符串之后，存在 2016 HPED字符串时，只比较前128，

                # 存在，只比较前128
                if match1 or match2 or (is_all_zero_or_f(compare_string) and is_all_zero_or_f(
                        standard_string)) or isHavingMellanox or '2016 HPED' in hex_to_string(
                    compare_string) or is127FMA627L3415 or isHavingQSFPDD2Q56PC015:
                    if (index >= 0 and index <= 127):
                        if char1 != char2:
                            differences.append(i // 2)
                            compared_string += "<span style='color: red;font-weight: bold;'>{}</span> ".format(char2)
                        else:
                            compared_string += char2 + " "
                    else:
                        compared_string += char2 + " "
                else:  # 全部比较

                    if char1 != char2:
                        # 新增规则
                        # 当compare_string后128位全是FF
                        # standard_string中Byte191位是C1, Byte223为E1时，Byte191位和Byte223不比较
                        if is_all_zero_or_f(compare_string) and (
                                (i == 382 and char1 == 'C1') or (i == 446 and char1 == 'E1')):
                            compared_string += char2 + " "
                        else:
                            differences.append(i // 2)
                            compared_string += "<span style='color: red;font-weight: bold;'>{}</span> ".format(char2)
                    else:
                        compared_string += char2 + " "
            # 按照索引来简单一些
            elif start_with_03_and_Byte2_is_00_21_22_23:

                # specialCodePathDirName 判断当前的兼容类型H3、HU，则比较前128位和后128位，否则还是比较 0-255
                if 'H3' in specialCodePathDirName or 'HU' in specialCodePathDirName:
                    # 前128位 (index >= 0 and index <= 127)
                    # 后128位 i > len(standard_string) - 256     并且后128位不能为F/0 standard_string[-256:] 取的是后128位
                    if (index >= 0 and index <= 127) or (i >= len(standard_string) - 256 and len(
                            standard_string[-256:].replace('F', '').replace('0', '')) > 0):
                        if char1 != char2:
                            differences.append(i // 2)
                            compared_string += "<span style='color: red;font-weight: bold;'>{}</span> ".format(char2)
                        else:
                            compared_string += char2 + " "
                    else:
                        compared_string += char2 + " "
                else:
                    # 只比较前256位
                    if (index >= 0 and index <= 255):
                        if char1 != char2:
                            differences.append(i // 2)
                            compared_string += "<span style='color: red;font-weight: bold;'>{}</span> ".format(char2)
                        else:
                            compared_string += char2 + " "
                    else:
                        compared_string += char2 + " "
            # 低速率(03、0B)封装产品排除后8位，不进行比对。只比对A0低位0~127/A0高位128~255/A2高位128~247
            elif start_with_03_or_0B:
                # 当左侧A0高位出现 "HP ProCurve Proprietary"时，只比对前128位与后128位，
                if standard_string.startswith("03") and len(
                        standard_string) == 768 and 'HP ProCurve' in get_middle_part(compare_string_parse,
                                                                                     num_parts=3) and 'HP ProCurve' in get_middle_part(
                        standard_string_parse, num_parts=3):
                    if (index >= 0 and index <= 127) or (index >= 256 and index <= 383):
                        if char1 != char2:
                            differences.append(i // 2)
                            compared_string += "<span style='color: red;font-weight: bold;'>{}</span> ".format(char2)
                        else:
                            compared_string += char2 + " "
                    else:
                        compared_string += char2 + " "
                # 当出现CMUIARACAA10-3227-01V01 字符时只比较前256位，后128不比较
                elif 'CMUIARACAA10-322' in compare_string_parse:
                    if index >= 0 and index <= 255:
                        if char1 != char2:
                            differences.append(i // 2)
                            compared_string += "<span style='color: red;font-weight: bold;'>{}</span> ".format(char2)
                        else:
                            compared_string += char2 + " "
                    else:
                        compared_string += char2 + " "
                # "GPON产品写码组合为128，后256位无需写入"
                # 当右侧出现“ 00056 ”字样时，复检只比对前128位，后256位无需比对
                elif '00056' in standard_string_parse:
                    if index >= 0 and index <= 127:
                        if char1 != char2:
                            differences.append(i // 2)
                            compared_string += "<span style='color: red;font-weight: bold;'>{}</span> ".format(char2)
                        else:
                            compared_string += char2 + " "
                    else:
                        compared_string += char2 + " "
                else:
                    # If "03" or "0B" is at the beginning of standard_string, skip the last 16 characters (8 bytes)
                    if i < len(standard_string) - 16:
                        if char1 != char2:
                            differences.append(i // 2)
                            compared_string += "<span style='color: red;font-weight: bold;'>{}</span> ".format(char2)
                        else:
                            compared_string += char2 + " "
                    else:
                        compared_string += char2 + " "
            # QDD（18、19、E1）
            # "封装为：（18.19.1E) 时，lower page00：byte 0-1 ，byte 85-117，
            # upper page00H：byte 128-255
            # upper page01H： byte 259-260，byte 266-269、byte 328-329，byte 351-378
            # upper page03H：byte 512-639
            # 只比对以上位置"
            elif start_with_18_or_19_or_1E:
                # If "18" or "19" is at the beginning of standard_string, compare only specified positions
                if index in (0, 1) or (index >= 85 and index <= 117) or (index >= 128 and index <= 255) or (
                        index >= 259 and index <= 260) or (index >= 266 and index <= 269) or (
                        index >= 328 and index <= 329) or (index >= 351 and index <= 378) or (
                        index >= 512 and index <= 639):
                    if char1 != char2:
                        differences.append(i // 2)
                        compared_string += "<span style='color: red;font-weight: bold;'>{}</span> ".format(char2)
                    else:
                        compared_string += char2 + " "
                else:
                    compared_string += char2 + " "

            elif strat_with_06:
                # 当右侧码位出现“WMOTCUXAAA10-1989-03V03“字样时，复检只比对前128位，后128位无需对比
                if 'WMOTCUXAAA10-1989-03V03' in standard_string_parse:
                    if index >= 0 and index <= 127:
                        if char1 != char2:
                            differences.append(i // 2)
                            compared_string += "<span style='color: red;font-weight: bold;'>{}</span> ".format(char2)
                        else:
                            compared_string += char2 + " "
                    else:
                        compared_string += char2 + " "
                else:
                    if char1 != char2:
                        differences.append(i // 2)
                        compared_string += "<span style='color: red;font-weight: bold;'>{}</span> ".format(char2)
                    else:
                        compared_string += char2 + " "
            else:
                # Compare all characters for other cases
                if char1 != char2:
                    differences.append(i // 2)
                    compared_string += "<span style='color: red;font-weight: bold;'>{}</span> ".format(char2)
                else:
                    compared_string += char2 + " "

            if (i + 2) % 32 == 0:
                compared_string += "<br>"

    return compared_string.strip(), differences

